{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6bbb4017",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "c6bf2f9e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from pandas.tseries import offsets\n",
    "from pandas.tseries.frequencies import to_offset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "606cb285",
   "metadata": {},
   "outputs": [],
   "source": [
    "class TimeFeature:\n",
    "    def __init__(self):\n",
    "        pass\n",
    "\n",
    "    def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:\n",
    "        \"\"\"\n",
    "        从时间索引中提取时间特征的核心方法\n",
    "        \n",
    "        参数:\n",
    "            index (pd.DatetimeIndex): 输入的时间索引数据，包含需要提取特征的时间戳\n",
    "            \n",
    "        返回:\n",
    "            np.ndarray: 提取到的时间特征数组，形状通常为 (len(index), feature_dim)\n",
    "        \"\"\"\n",
    "        pass\n",
    "\n",
    "    def __repr__(self):\n",
    "        \"\"\"\n",
    "        返回对象的规范字符串表示（用于调试和打印）\n",
    "        \n",
    "        返回:\n",
    "            str: 类名加空括号的字符串形式（如 \"TimeFeature()\"）\n",
    "        \"\"\"\n",
    "        return self.__class__.__name__ + \"()\"\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "eace7d97",
   "metadata": {},
   "outputs": [],
   "source": [
    "class SecondOfMinute(TimeFeature):\n",
    "    \"\"\"Minute of hour encoded as value between [-0.5, 0.5]\"\"\"\n",
    "\n",
    "    def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:\n",
    "        return index.second / 59.0 - 0.5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "18bab9ff",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始时间索引:\n",
      "DatetimeIndex(['2023-01-01 00:00:00', '2023-01-01 00:00:01',\n",
      "               '2023-01-01 00:00:02'],\n",
      "              dtype='datetime64[ns]', freq='s')\n",
      "\n",
      "标准化后的秒特征:\n",
      "Index([-0.5, -0.4830508474576271, -0.4661016949152542], dtype='float64')\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\Henan\\AppData\\Local\\Temp\\ipykernel_24408\\1106856608.py:3: FutureWarning: 'S' is deprecated and will be removed in a future version, please use 's' instead.\n",
      "  index = pd.date_range(\n"
     ]
    }
   ],
   "source": [
    "def SecondOfMinute_test():\n",
    "    # 创建一个包含3个时间点的DatetimeIndex\n",
    "    index = pd.date_range(\n",
    "        start=\"2023-01-01 00:00:00\", periods=3, freq=\"S\"\n",
    "    )  # 每秒一个时间点\n",
    "\n",
    "    # 输出原始时间索引\n",
    "    print(\"原始时间索引:\")\n",
    "    print(index)\n",
    "\n",
    "    # 创建秒特征提取器实例\n",
    "    feature_extractor = SecondOfMinute()\n",
    "\n",
    "    # 提取标准化后的秒特征\n",
    "    features = feature_extractor(index)\n",
    "\n",
    "    # 输出标准化后的特征\n",
    "    print(\"\\n标准化后的秒特征:\")\n",
    "    print(features)\n",
    "SecondOfMinute_test()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "8c2a0727",
   "metadata": {},
   "outputs": [],
   "source": [
    "class MinuteOfHour(TimeFeature):\n",
    "    \"\"\"Minute of hour encoded as value between [-0.5, 0.5]\"\"\"\n",
    "\n",
    "    def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:\n",
    "        return index.minute / 59.0 - 0.5\n",
    "\n",
    "\n",
    "class HourOfDay(TimeFeature):\n",
    "    \"\"\"Hour of day encoded as value between [-0.5, 0.5]\"\"\"\n",
    "\n",
    "    def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:\n",
    "        return index.hour / 23.0 - 0.5\n",
    "\n",
    "\n",
    "class DayOfWeek(TimeFeature):\n",
    "    \"\"\"Hour of day encoded as value between [-0.5, 0.5]\"\"\"\n",
    "\n",
    "    def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:\n",
    "        return index.dayofweek / 6.0 - 0.5\n",
    "\n",
    "\n",
    "class DayOfMonth(TimeFeature):\n",
    "    \"\"\"Day of month encoded as value between [-0.5, 0.5]\"\"\"\n",
    "\n",
    "    def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:\n",
    "        return (index.day - 1) / 30.0 - 0.5\n",
    "\n",
    "\n",
    "class DayOfYear(TimeFeature):\n",
    "    \"\"\"Day of year encoded as value between [-0.5, 0.5]\"\"\"\n",
    "\n",
    "    def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:\n",
    "        return (index.dayofyear - 1) / 365.0 - 0.5\n",
    "\n",
    "\n",
    "class MonthOfYear(TimeFeature):\n",
    "    \"\"\"Month of year encoded as value between [-0.5, 0.5]\"\"\"\n",
    "\n",
    "    def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:\n",
    "        return (index.month - 1) / 11.0 - 0.5\n",
    "\n",
    "\n",
    "class WeekOfYear(TimeFeature):\n",
    "    \"\"\"Week of year encoded as value between [-0.5, 0.5]\"\"\"\n",
    "\n",
    "    def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:\n",
    "        # 使用isocalendar().week替代week属性，兼容新版本Pandas\n",
    "        return (index.isocalendar().week - 1) / 52.0 - 0.5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "bff4a164",
   "metadata": {},
   "outputs": [],
   "source": [
    "def time_features_from_frequency_str(freq_str: str) -> List[TimeFeature]:\n",
    "    \"\"\"\n",
    "    Returns a list of time features that will be appropriate for the given frequency string.\n",
    "    Parameters\n",
    "    ----------\n",
    "    freq_str\n",
    "        Frequency string of the form [multiple][granularity] such as \"12H\", \"5min\", \"1D\" etc.\n",
    "    \"\"\"\n",
    "\n",
    "    features_by_offsets = {\n",
    "        offsets.YearEnd: [],\n",
    "        offsets.QuarterEnd: [MonthOfYear],\n",
    "        offsets.MonthEnd: [MonthOfYear],\n",
    "        offsets.Week: [DayOfMonth, WeekOfYear],\n",
    "        offsets.Day: [DayOfWeek, DayOfMonth, DayOfYear],\n",
    "        offsets.BusinessDay: [DayOfWeek, DayOfMonth, DayOfYear],\n",
    "        offsets.Hour: [HourOfDay, DayOfWeek, DayOfMonth, DayOfYear],\n",
    "        offsets.Minute: [\n",
    "            MinuteOfHour,\n",
    "            HourOfDay,\n",
    "            DayOfWeek,\n",
    "            DayOfMonth,\n",
    "            DayOfYear,\n",
    "        ],\n",
    "        offsets.Second: [\n",
    "            SecondOfMinute,\n",
    "            MinuteOfHour,\n",
    "            HourOfDay,\n",
    "            DayOfWeek,\n",
    "            DayOfMonth,\n",
    "            DayOfYear,\n",
    "        ],\n",
    "    }\n",
    "\n",
    "    offset = to_offset(freq_str)\n",
    "\n",
    "    for offset_type, feature_classes in features_by_offsets.items():\n",
    "        if isinstance(offset, offset_type):\n",
    "            return [cls() for cls in feature_classes]\n",
    "\n",
    "    supported_freq_msg = f\"\"\"\n",
    "    Unsupported frequency {freq_str}\n",
    "    The following frequencies are supported:\n",
    "        Y   - yearly\n",
    "            alias: A\n",
    "        M   - monthly\n",
    "        W   - weekly\n",
    "        D   - daily\n",
    "        B   - business days\n",
    "        H   - hourly\n",
    "        T   - minutely\n",
    "            alias: min\n",
    "        S   - secondly\n",
    "    \"\"\"\n",
    "    raise RuntimeError(supported_freq_msg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "4596d6b2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "频率: Y\n",
      "时间索引: DatetimeIndex(['2020-12-31', '2021-12-31'], dtype='datetime64[ns]', freq='YE-DEC')\n",
      "\n",
      "频率: M\n",
      "时间索引: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='ME')\n",
      "  特征: MonthOfYear(), 形状: (3,), 示例: Index([-0.5, -0.40909090909090906], dtype='float64')\n",
      "\n",
      "频率: W\n",
      "时间索引: DatetimeIndex(['2020-01-05', '2020-01-12'], dtype='datetime64[ns]', freq='W-SUN')\n",
      "  特征: DayOfMonth(), 形状: (2,), 示例: Index([-0.3666666666666667, -0.13333333333333336], dtype='float64')\n",
      "  特征: WeekOfYear(), 形状: (2,), 示例: 2020-01-05        -0.5\n",
      "2020-01-12   -0.480769\n",
      "Freq: W-SUN, Name: week, dtype: Float64\n",
      "\n",
      "频率: D\n",
      "时间索引: DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04'], dtype='datetime64[ns]', freq='D')\n",
      "  特征: DayOfWeek(), 形状: (4,), 示例: Index([-0.16666666666666669, 0.0], dtype='float64')\n",
      "  特征: DayOfMonth(), 形状: (4,), 示例: Index([-0.5, -0.4666666666666667], dtype='float64')\n",
      "  特征: DayOfYear(), 形状: (4,), 示例: Index([-0.5, -0.49726027397260275], dtype='float64')\n",
      "\n",
      "频率: H\n",
      "时间索引: DatetimeIndex(['2020-01-01 00:00:00', '2020-01-01 01:00:00',\n",
      "               '2020-01-01 02:00:00', '2020-01-01 03:00:00',\n",
      "               '2020-01-01 04:00:00'],\n",
      "              dtype='datetime64[ns]', freq='h')\n",
      "  特征: HourOfDay(), 形状: (5,), 示例: Index([-0.5, -0.4565217391304348], dtype='float64')\n",
      "  特征: DayOfWeek(), 形状: (5,), 示例: Index([-0.16666666666666669, -0.16666666666666669], dtype='float64')\n",
      "  特征: DayOfMonth(), 形状: (5,), 示例: Index([-0.5, -0.5], dtype='float64')\n",
      "  特征: DayOfYear(), 形状: (5,), 示例: Index([-0.5, -0.5], dtype='float64')\n",
      "\n",
      "频率: T\n",
      "时间索引: DatetimeIndex(['2020-01-01 00:00:00', '2020-01-01 00:01:00',\n",
      "               '2020-01-01 00:02:00'],\n",
      "              dtype='datetime64[ns]', freq='min')\n",
      "  特征: MinuteOfHour(), 形状: (3,), 示例: Index([-0.5, -0.4830508474576271], dtype='float64')\n",
      "  特征: HourOfDay(), 形状: (3,), 示例: Index([-0.5, -0.5], dtype='float64')\n",
      "  特征: DayOfWeek(), 形状: (3,), 示例: Index([-0.16666666666666669, -0.16666666666666669], dtype='float64')\n",
      "  特征: DayOfMonth(), 形状: (3,), 示例: Index([-0.5, -0.5], dtype='float64')\n",
      "  特征: DayOfYear(), 形状: (3,), 示例: Index([-0.5, -0.5], dtype='float64')\n",
      "\n",
      "频率: S\n",
      "时间索引: DatetimeIndex(['2020-01-01 00:00:00', '2020-01-01 00:00:01',\n",
      "               '2020-01-01 00:00:02'],\n",
      "              dtype='datetime64[ns]', freq='s')\n",
      "  特征: SecondOfMinute(), 形状: (3,), 示例: Index([-0.5, -0.4830508474576271], dtype='float64')\n",
      "  特征: MinuteOfHour(), 形状: (3,), 示例: Index([-0.5, -0.5], dtype='float64')\n",
      "  特征: HourOfDay(), 形状: (3,), 示例: Index([-0.5, -0.5], dtype='float64')\n",
      "  特征: DayOfWeek(), 形状: (3,), 示例: Index([-0.16666666666666669, -0.16666666666666669], dtype='float64')\n",
      "  特征: DayOfMonth(), 形状: (3,), 示例: Index([-0.5, -0.5], dtype='float64')\n",
      "  特征: DayOfYear(), 形状: (3,), 示例: Index([-0.5, -0.5], dtype='float64')\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\Henan\\AppData\\Local\\Temp\\ipykernel_24408\\1480872171.py:4: FutureWarning: 'Y' is deprecated and will be removed in a future version, please use 'YE' instead.\n",
      "  'Y': pd.date_range('2020-01-01', periods=2, freq='Y'),  # 年\n",
      "C:\\Users\\Henan\\AppData\\Local\\Temp\\ipykernel_24408\\1480872171.py:5: FutureWarning: 'M' is deprecated and will be removed in a future version, please use 'ME' instead.\n",
      "  'M': pd.date_range('2020-01-01', periods=3, freq='M'),  # 月\n",
      "C:\\Users\\Henan\\AppData\\Local\\Temp\\ipykernel_24408\\1480872171.py:8: FutureWarning: 'H' is deprecated and will be removed in a future version, please use 'h' instead.\n",
      "  'H': pd.date_range('2020-01-01', periods=5, freq='H'),  # 小时\n",
      "C:\\Users\\Henan\\AppData\\Local\\Temp\\ipykernel_24408\\1480872171.py:9: FutureWarning: 'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
      "  'T': pd.date_range('2020-01-01 00:00', periods=3, freq='T'),  # 分钟\n",
      "C:\\Users\\Henan\\AppData\\Local\\Temp\\ipykernel_24408\\1480872171.py:10: FutureWarning: 'S' is deprecated and will be removed in a future version, please use 's' instead.\n",
      "  'S': pd.date_range('2020-01-01 00:00:00', periods=3, freq='S'),  # 秒\n",
      "C:\\Users\\Henan\\AppData\\Local\\Temp\\ipykernel_24408\\2044244163.py:35: FutureWarning: 'Y' is deprecated and will be removed in a future version, please use 'YE' instead.\n",
      "  offset = to_offset(freq_str)\n",
      "C:\\Users\\Henan\\AppData\\Local\\Temp\\ipykernel_24408\\2044244163.py:35: FutureWarning: 'M' is deprecated and will be removed in a future version, please use 'ME' instead.\n",
      "  offset = to_offset(freq_str)\n",
      "C:\\Users\\Henan\\AppData\\Local\\Temp\\ipykernel_24408\\2044244163.py:35: FutureWarning: 'H' is deprecated and will be removed in a future version, please use 'h' instead.\n",
      "  offset = to_offset(freq_str)\n",
      "C:\\Users\\Henan\\AppData\\Local\\Temp\\ipykernel_24408\\2044244163.py:35: FutureWarning: 'T' is deprecated and will be removed in a future version, please use 'min' instead.\n",
      "  offset = to_offset(freq_str)\n",
      "C:\\Users\\Henan\\AppData\\Local\\Temp\\ipykernel_24408\\2044244163.py:35: FutureWarning: 'S' is deprecated and will be removed in a future version, please use 's' instead.\n",
      "  offset = to_offset(freq_str)\n"
     ]
    }
   ],
   "source": [
    "def test_time_features_from_frequency_str():\n",
    "    # 创建不同频率的时间索引\n",
    "    freqs = {\n",
    "        'Y': pd.date_range('2020-01-01', periods=2, freq='Y'),  # 年\n",
    "        'M': pd.date_range('2020-01-01', periods=3, freq='M'),  # 月\n",
    "        'W': pd.date_range('2020-01-01', periods=2, freq='W'),  # 周\n",
    "        'D': pd.date_range('2020-01-01', periods=4, freq='D'),  # 日\n",
    "        'H': pd.date_range('2020-01-01', periods=5, freq='H'),  # 小时\n",
    "        'T': pd.date_range('2020-01-01 00:00', periods=3, freq='T'),  # 分钟\n",
    "        'S': pd.date_range('2020-01-01 00:00:00', periods=3, freq='S'),  # 秒\n",
    "    }\n",
    "\n",
    "    # 为每个频率生成特征并展示结果\n",
    "    for freq_str, index in freqs.items():\n",
    "        print(f\"\\n频率: {freq_str}\")\n",
    "        print(f\"时间索引: {index}\")\n",
    "        \n",
    "        # 获取对应的时间特征提取器列表\n",
    "        feature_extractors = time_features_from_frequency_str(freq_str)\n",
    "        \n",
    "        # 应用每个特征提取器\n",
    "        for extractor in feature_extractors:\n",
    "            features = extractor(index)\n",
    "            print(f\"  特征: {extractor}, 形状: {features.shape}, 示例: {features[:2]}\")\n",
    "test_time_features_from_frequency_str()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "8dc2ebbf",
   "metadata": {},
   "outputs": [],
   "source": [
    "def time_features(dates, timeenc=1, freq=\"h\"):\n",
    "    \"\"\"\n",
    "    > `time_features` takes in a `dates` dataframe with a 'dates' column and extracts the date down to `freq` where freq can be any of the following if `timeenc` is 0:\n",
    "    > * m - [month]\n",
    "    > * w - [month]\n",
    "    > * d - [month, day, weekday]\n",
    "    > * b - [month, day, weekday]\n",
    "    > * h - [month, day, weekday, hour]\n",
    "    > * t - [month, day, weekday, hour, *minute]\n",
    "    >\n",
    "    > If `timeenc` is 1, a similar, but different list of `freq` values are supported (all encoded between [-0.5 and 0.5]):\n",
    "    > * Q - [month]\n",
    "    > * M - [month]\n",
    "    > * W - [Day of month, week of year]\n",
    "    > * D - [Day of week, day of month, day of year]\n",
    "    > * B - [Day of week, day of month, day of year]\n",
    "    > * H - [Hour of day, day of week, day of month, day of year]\n",
    "    > * T - [Minute of hour*, hour of day, day of week, day of month, day of year]\n",
    "    > * S - [Second of minute, minute of hour, hour of day, day of week, day of month, day of year]\n",
    "\n",
    "    *minute returns a number from 0-3 corresponding to the 15 minute period it falls into.\n",
    "    \"\"\"\n",
    "    if timeenc == 0:\n",
    "        dates[\"month\"] = dates.date.apply(lambda row: row.month, 1)\n",
    "        dates[\"day\"] = dates.date.apply(lambda row: row.day, 1)\n",
    "        dates[\"weekday\"] = dates.date.apply(lambda row: row.weekday(), 1)\n",
    "        dates[\"hour\"] = dates.date.apply(lambda row: row.hour, 1)\n",
    "        dates[\"minute\"] = dates.date.apply(lambda row: row.minute, 1)\n",
    "        dates[\"minute\"] = dates.minute.map(lambda x: x // 15)\n",
    "        freq_map = {\n",
    "            \"y\": [],\n",
    "            \"m\": [\"month\"],\n",
    "            \"w\": [\"month\"],\n",
    "            \"d\": [\"month\", \"day\", \"weekday\"],\n",
    "            \"b\": [\"month\", \"day\", \"weekday\"],\n",
    "            \"h\": [\"month\", \"day\", \"weekday\", \"hour\"],\n",
    "            \"t\": [\"month\", \"day\", \"weekday\", \"hour\", \"minute\"],\n",
    "        }\n",
    "        return dates[freq_map[freq.lower()]].values\n",
    "    if timeenc == 1:\n",
    "        dates = pd.to_datetime(dates.date.values)\n",
    "        return np.vstack(\n",
    "            [feat(dates) for feat in time_features_from_frequency_str(freq)]\n",
    "        ).transpose(1, 0)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "my_env",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
